package com.smallstep.hystrix.client;


import com.smallstep.hystrix.entity.FFRequestInfo;
import com.smallstep.hystrix.enums.ReturnModeEnum;
import com.smallstep.hystrix.executor.FFHystrixHttpClientExecutor;
import org.apache.http.client.HttpClient;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StopWatch;
import org.springframework.web.client.RequestCallback;
import org.springframework.web.client.ResponseExtractor;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriTemplate;

import java.lang.reflect.Type;
import java.net.URI;
import java.util.*;

/**
 * @author 李俊
 */
public class FFHttpClient {

    protected FFRestTemplate restTemplate;

    public FFHttpClient() {
        HttpClient httpClient = FFHttpClientSingleton.getInstance().getHttpClient();
        HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient);
        restTemplate = new FFRestTemplate(factory);
    }


    public RestTemplate getRestTemplate() {
        return this.restTemplate;
    }

    protected HttpHeaders mergerHeaders(URI uri, HttpMethod method, HttpHeaders headers) {
        return headers;
    }

    public void post(String url, Object requestBody, Object... uriVariables) {
        HttpEntity<Object> httpEntity = this.getHttpEntity(requestBody);
        this.execute(url, HttpMethod.POST, httpEntity, uriVariables);
    }

    public void post(String url, Object requestBody, Map<String, ?> uriVariables) {
        HttpEntity<Object> httpEntity = this.getHttpEntity(requestBody);
        this.execute(url, HttpMethod.POST, httpEntity, uriVariables);
    }

    public <T> T getForObject(String url, Type responseType, Map<String, ?> uriVariables) {
        HttpEntity<Object> httpEntity = this.getHttpEntity((Object)null);
        return this.executeForObject(url, HttpMethod.GET, httpEntity, responseType, uriVariables);
    }

    public <T> ResponseEntity<T> getForEntity(String url, Type responseType, Map<String, ?> uriVariables) {
        HttpEntity<Object> httpEntity = this.getHttpEntity((Object)null);
        return this.executeForEntity(url, HttpMethod.GET, httpEntity, responseType, uriVariables);
    }

    public void put(String url, Object requestBody, Object... uriVariables) {
        HttpEntity<Object> httpEntity = this.getHttpEntity(requestBody);
        this.execute(url, HttpMethod.PUT, httpEntity, uriVariables);
    }

    public <T> T getForObject(String url, Type responseType, Object... uriVariables) {
        HttpEntity<Object> httpEntity = this.getHttpEntity((Object)null);
        return this.executeForObject(url, HttpMethod.GET, httpEntity, responseType, uriVariables);
    }

    public <T> ResponseEntity<T> getForEntity(String url, Type responseType, Object... uriVariables) {
        HttpEntity<Object> httpEntity = this.getHttpEntity((Object)null);
        return this.executeForEntity(url, HttpMethod.GET, httpEntity, responseType, uriVariables);
    }

    public <T> T putForObject(String url, Object requestBody, Type responseType, Object... uriVariables) {
        HttpEntity<Object> httpEntity = this.getHttpEntity(requestBody);
        return this.executeForObject(url, HttpMethod.PUT, httpEntity, responseType, uriVariables);
    }

    public <T> ResponseEntity<T> putForEntity(String url, Object requestBody, Type responseType, Object... uriVariables) {
        HttpEntity<Object> httpEntity = this.getHttpEntity(requestBody);
        return this.executeForEntity(url, HttpMethod.PUT, httpEntity, responseType, uriVariables);
    }

    public void put(String url, Object requestBody, Map<String, ?> uriVariables) {
        HttpEntity<Object> httpEntity = this.getHttpEntity(requestBody);
        this.execute(url, HttpMethod.PUT, httpEntity, uriVariables);
    }

    public <T> T putForObject(String url, Object requestBody, Type responseType, Map<String, ?> uriVariables) {
        HttpEntity<Object> httpEntity = this.getHttpEntity(requestBody);
        return this.executeForObject(url, HttpMethod.PUT, httpEntity, responseType, uriVariables);
    }

    public <T> T postForObject(String url, Object requestBody, Type responseType, Object... uriVariables) {
        HttpEntity<Object> httpEntity = this.getHttpEntity(requestBody);
        return this.executeForObject(url, HttpMethod.POST, httpEntity, responseType, uriVariables);
    }

    public <T> T postForObject(String url, Object requestBody, Type responseType, Map<String, ?> uriVariables) {
        HttpEntity<Object> httpEntity = this.getHttpEntity(requestBody);
        return this.executeForObject(url, HttpMethod.POST, httpEntity, responseType, uriVariables);
    }

    public <T> ResponseEntity<T> postForEntity(String url, Object requestBody, Type responseType, Map<String, ?> uriVariables) {
        HttpEntity<Object> httpEntity = this.getHttpEntity(requestBody);
        return this.executeForEntity(url, HttpMethod.POST, httpEntity, responseType, uriVariables);
    }

    public <T> ResponseEntity<T> putForEntity(String url, Object requestBody, Type responseType, Map<String, ?> uriVariables) {
        HttpEntity<Object> httpEntity = this.getHttpEntity(requestBody);
        return this.executeForEntity(url, HttpMethod.PUT, httpEntity, responseType, uriVariables);
    }

    public void delete(String url, Object... uriVariables) {
        HttpEntity<Object> httpEntity = this.getHttpEntity((Object)null);
        this.execute(url, HttpMethod.DELETE, httpEntity, uriVariables);
    }

    public void delete(String url, Map<String, ?> uriVariables) {
        HttpEntity<Object> httpEntity = this.getHttpEntity((Object)null);
        this.execute(url, HttpMethod.DELETE, httpEntity, uriVariables);
    }

    public <T> ResponseEntity<T> postForEntity(String url, Object requestBody, Type responseType, Object... uriVariables) {
        HttpEntity<Object> httpEntity = this.getHttpEntity(requestBody);
        return this.executeForEntity(url, HttpMethod.POST, httpEntity, responseType, uriVariables);
    }

    public <T> T deleteForObject(String url, Type responseType, Object... uriVariables) {
        HttpEntity<Object> httpEntity = this.getHttpEntity((Object)null);
        return this.executeForObject(url, HttpMethod.DELETE, httpEntity, responseType, uriVariables);
    }

    public <T> T deleteForObject(String url, Type responseType, Map<String, ?> uriVariables) {
        HttpEntity<Object> httpEntity = this.getHttpEntity((Object)null);
        return this.executeForObject(url, HttpMethod.DELETE, httpEntity, responseType, uriVariables);
    }

    public <T> ResponseEntity<T> deleteForEntity(String url, Type responseType, Object... uriVariables) {
        HttpEntity<Object> httpEntity = this.getHttpEntity((Object)null);
        return this.executeForEntity(url, HttpMethod.DELETE, httpEntity, responseType, uriVariables);
    }

    public <T> ResponseEntity<T> deleteForEntity(String url, Type responseType, Map<String, ?> uriVariables) {
        HttpEntity<Object> httpEntity = this.getHttpEntity((Object)null);
        return this.executeForEntity(url, HttpMethod.DELETE, httpEntity, responseType, uriVariables);
    }

    public <T> T executeForObject(String url, HttpMethod method, HttpEntity<?> requestEntity, Type responseType, Object... uriVariables) {
        URI uri = (new UriTemplate(url)).expand(uriVariables);
        return (new FFHystrixHttpClientExecutor<T>(new FFRequestInfo(ReturnModeEnum.OBJECT, url, method, requestEntity, responseType, uriVariables), uri) {
            @Override
            protected T run(URI uri, HttpMethod method, HttpEntity requestEntity, Type responseType) {
                return FFHttpClient.this.doExecuteForObject(uri, method, requestEntity, responseType);
            }
        }).execute();
    }

    public <T> T executeForObject(String url, HttpMethod method, HttpEntity<?> requestEntity, Type responseType, Map<String, ?> uriVariables) {
        URI uri = (new UriTemplate(url)).expand(uriVariables);
        return (new FFHystrixHttpClientExecutor<T>(new FFRequestInfo(ReturnModeEnum.OBJECT, url, method, requestEntity, responseType, uriVariables), uri) {
            @Override
            protected T run(URI uri, HttpMethod method, HttpEntity requestEntity, Type responseType) {
                return FFHttpClient.this.doExecuteForObject(uri, method, requestEntity, responseType);
            }
        }).execute();
    }

    public <T> T executeForObject(URI uri, HttpMethod method, HttpEntity<?> requestEntity, Type responseType) {
        return (new FFHystrixHttpClientExecutor<T>(new FFRequestInfo(ReturnModeEnum.OBJECT, uri.toString(), method, requestEntity, responseType), uri) {
            @Override
            protected T run(URI uri, HttpMethod method, HttpEntity requestEntity, Type responseType) {
                return FFHttpClient.this.doExecuteForObject(uri, method, requestEntity, responseType);
            }
        }).execute();
    }

    private <T> T doExecuteForObject(URI uri, HttpMethod method, HttpEntity<?> requestEntity, Type responseType) {
        HttpEntity<?> mergeRequestEntity = this.merge(uri, method, requestEntity);
        RequestCallback requestCallback = this.restTemplate.httpEntityCallback(mergeRequestEntity, responseType);
        ResponseExtractor<T> responseExtractor = this.restTemplate.httpMessageConverterExtractor(responseType);
        return this.doExecute(uri, method, requestCallback, responseExtractor);
    }

    public <T> ResponseEntity<T> executeForEntity(String url, HttpMethod method, HttpEntity<?> requestEntity, Type responseType, Object... uriVariables) {
        URI uri = (new UriTemplate(url)).expand(uriVariables);
        return (ResponseEntity)(new FFHystrixHttpClientExecutor<ResponseEntity<T>>(new FFRequestInfo(ReturnModeEnum.RESPONSE_ENTITY, url, method, requestEntity, responseType, uriVariables), uri) {
            @Override
            protected ResponseEntity<T> run(URI uri, HttpMethod method, HttpEntity requestEntity, Type responseType) {
                return FFHttpClient.this.doExecuteForEntity(uri, method, requestEntity, responseType);
            }
        }).execute();
    }

    public <T> ResponseEntity<T> executeForEntity(String url, HttpMethod method, HttpEntity<?> requestEntity, Type responseType, Map<String, ?> uriVariables) {
        URI uri = (new UriTemplate(url)).expand(uriVariables);
        return (ResponseEntity)(new FFHystrixHttpClientExecutor<ResponseEntity<T>>(new FFRequestInfo(ReturnModeEnum.RESPONSE_ENTITY, url, method, requestEntity, responseType, uriVariables), uri) {
            @Override
            protected ResponseEntity<T> run(URI uri, HttpMethod method, HttpEntity requestEntity, Type responseType) {
                return FFHttpClient.this.doExecuteForEntity(uri, method, requestEntity, responseType);
            }
        }).execute();
    }

    public <T> ResponseEntity<T> executeForEntity(URI uri, HttpMethod method, HttpEntity<?> requestEntity, Type responseType) {
        return (ResponseEntity)(new FFHystrixHttpClientExecutor<ResponseEntity<T>>(new FFRequestInfo(ReturnModeEnum.RESPONSE_ENTITY, uri.toString(), method, requestEntity, responseType), uri) {
            @Override
            protected ResponseEntity<T> run(URI uri, HttpMethod method, HttpEntity requestEntity, Type responseType) {
                return FFHttpClient.this.doExecuteForEntity(uri, method, requestEntity, responseType);
            }
        }).execute();
    }

    private <T> ResponseEntity<T> doExecuteForEntity(URI uri, HttpMethod method, HttpEntity<?> requestEntity, Type responseType) {
        HttpEntity<?> mergeRequestEntity = this.merge(uri, method, requestEntity);
        RequestCallback requestCallback = this.restTemplate.httpEntityCallback(mergeRequestEntity, responseType);
        ResponseExtractor<ResponseEntity<T>> responseExtractor = this.restTemplate.responseEntityExtractor(responseType);
        return (ResponseEntity)this.doExecute(uri, method, requestCallback, responseExtractor);
    }

    public void execute(String url, HttpMethod method, HttpEntity<?> requestEntity, Object... uriVariables) {
        URI uri = (new UriTemplate(url)).expand(uriVariables);
        (new FFHystrixHttpClientExecutor(new FFRequestInfo(ReturnModeEnum.VOID, url, method, requestEntity, (Type)null, uriVariables), uri) {
            @Override
            protected Object run(URI uri, HttpMethod method, HttpEntity requestEntity, Type responseType) {
                FFHttpClient.this.doExecute(uri, method, requestEntity);
                return null;
            }
        }).execute();
    }

    public void execute(String url, HttpMethod method, HttpEntity<?> requestEntity, Map<String, ?> uriVariables) {
        URI uri = (new UriTemplate(url)).expand(uriVariables);
        (new FFHystrixHttpClientExecutor(new FFRequestInfo(ReturnModeEnum.VOID, url, method, requestEntity, (Type)null, uriVariables), uri) {
            @Override
            protected Object run(URI uri, HttpMethod method, HttpEntity requestEntity, Type responseType) {
                FFHttpClient.this.doExecute(uri, method, requestEntity);
                return null;
            }
        }).execute();
    }

    public void execute(URI uri, HttpMethod method, HttpEntity<?> requestEntity) {
        (new FFHystrixHttpClientExecutor(new FFRequestInfo(ReturnModeEnum.VOID, uri.toString(), method, requestEntity, (Type)null), uri) {
            @Override
            protected Object run(URI uri, HttpMethod method, HttpEntity requestEntity, Type responseType) {
                FFHttpClient.this.doExecute(uri, method, requestEntity);
                return null;
            }
        }).execute();
    }

    private void doExecute(URI uri, HttpMethod method, HttpEntity<?> requestEntity) {
        HttpEntity<?> mergeRequestEntity = this.merge(uri, method, requestEntity);
        RequestCallback requestCallback = this.restTemplate.httpEntityCallback(mergeRequestEntity);
        this.doExecute(uri, method, requestCallback, (ResponseExtractor)null);
    }

    protected HttpEntity<Object> getHttpEntity(Object requestBody) {
        return new HttpEntity(requestBody);
    }

    private HttpEntity<?> merge(final URI uri, HttpMethod method, HttpEntity<?> httpEntity) {
        final HttpHeaders httpHeaders = new HttpHeaders();
        HttpHeaders tempHeaders = httpEntity.getHeaders();
        if (null != tempHeaders) {
            httpHeaders.putAll(tempHeaders);
        }


        HttpHeaders mergerHeaders = this.mergerHeaders(uri, method, httpHeaders);
        return new HttpEntity(httpEntity.getBody(), mergerHeaders);
    }



    protected <T> T doExecute(URI url, HttpMethod method, RequestCallback requestCallback, ResponseExtractor<T> responseExtractor) {
        StopWatch sw = new StopWatch();
        sw.start();
        T result = this.restTemplate.execute(url, method, requestCallback, responseExtractor);
        sw.stop();
        return result;
    }

    /** @deprecated */
    @Deprecated
    public <T> T post(String url, Type responseType, Object... uriVariables) {
        return this.postForObject(url, (Object)null, responseType, (Object[])uriVariables);
    }

    /** @deprecated */
    @Deprecated
    public <T> T post(String url, Object requestBody, Type responseType, Object... uriVariables) {
        return this.postForObject(url, requestBody, responseType, uriVariables);
    }

    /** @deprecated */
    @Deprecated
    public <T> T post(String url, Type responseType, Map<String, ?> uriVariables) {
        return this.postForObject(url, (Object)null, responseType, (Map<String, ?>) uriVariables);
    }

    /** @deprecated */
    @Deprecated
    public <T> T post(String url, Object requestBody, Type responseType, Map<String, ?> uriVariables) {
        return this.postForObject(url, requestBody, responseType, uriVariables);
    }

    /** @deprecated */
    @Deprecated
    public <T> T post(String url, Type responseType) {
        return this.postForObject(url, (Object)null, responseType, (Object[])null);
    }

    /** @deprecated */
    @Deprecated
    public <T> T post(String url, Object requestBody, Type responseType) {
        return this.postForObject(url, requestBody, responseType);
    }

    /** @deprecated */
    @Deprecated
    public <T> T get(String url, Type responseType, Object... uriVariables) {
        return this.getForObject(url, responseType, uriVariables);
    }

    /** @deprecated */
    @Deprecated
    public <T> T get(String url, Object requestBody, Type responseType, Object... uriVariables) {
        return this.getForObject(url, responseType, uriVariables);
    }

    /** @deprecated */
    @Deprecated
    public <T> T get(String url, Type responseType, Map<String, ?> uriVariables) {
        return this.getForObject(url, responseType, uriVariables);
    }

    /** @deprecated */
    @Deprecated
    public <T> T get(String url, Object requestBody, Type responseType, Map<String, ?> uriVariables) {
        return this.getForObject(url, responseType, uriVariables);
    }

    /** @deprecated */
    @Deprecated
    public <T> T get(String url, Type responseType) {
        return this.getForObject(url, responseType);
    }

    /** @deprecated */
    @Deprecated
    public <T> T get(String url, Object requestBody, Type responseType) {
        return this.getForObject(url, responseType);
    }

    /** @deprecated */
    @Deprecated
    public <T> T delete(String url, Type responseType, Object... uriVariables) {
        return this.deleteForObject(url, responseType, uriVariables);
    }

    /** @deprecated */
    @Deprecated
    public <T> T delete(String url, Object requestBody, Type responseType, Object... uriVariables) {
        return this.deleteForObject(url, responseType, uriVariables);
    }

    /** @deprecated */
    @Deprecated
    public <T> T delete(String url, Type responseType, Map<String, ?> uriVariables) {
        return this.deleteForObject(url, responseType, uriVariables);
    }

    /** @deprecated */
    @Deprecated
    public <T> T delete(String url, Object requestBody, Type responseType, Map<String, ?> uriVariables) {
        return this.deleteForObject(url, responseType, uriVariables);
    }

    /** @deprecated */
    @Deprecated
    public <T> T delete(String url, Type responseType) {
        return this.deleteForObject(url, responseType);
    }

    /** @deprecated */
    @Deprecated
    public <T> T delete(String url, Object requestBody, Type responseType) {
        return this.deleteForObject(url, responseType);
    }

    /** @deprecated */
    @Deprecated
    public <T> T put(String url, Type responseType, Object... uriVariables) {
        return this.putForObject(url, (Object)null, responseType, (Object[])uriVariables);
    }

    /** @deprecated */
    @Deprecated
    public <T> T put(String url, Object requestBody, Type responseType, Object... uriVariables) {
        return this.putForObject(url, requestBody, responseType, uriVariables);
    }

    /** @deprecated */
    @Deprecated
    public <T> T put(String url, Type responseType, Map<String, ?> uriVariables) {
        return this.putForObject(url, (Object)null, responseType, (Map<String, ?>) uriVariables);
    }

    /** @deprecated */
    @Deprecated
    public <T> T put(String url, Object requestBody, Type responseType, Map<String, ?> uriVariables) {
        return this.putForObject(url, requestBody, responseType, uriVariables);
    }

    /** @deprecated */
    @Deprecated
    public <T> T put(String url, Type responseType) {
        return this.putForObject(url, (Object)null, responseType, (Object[])null);
    }

    /** @deprecated */
    @Deprecated
    public <T> T put(String url, Object requestBody, Type responseType) {
        return this.putForObject(url, requestBody, responseType);
    }

    /** @deprecated */
    @Deprecated
    public <T> T exchange(String url, HttpMethod method, Type responseType, Map<String, ?> uriVariables) {
        HttpEntity<Object> httpEntity = this.getHttpEntity((Object)null);
        return this.executeForObject(url, method, httpEntity, responseType, uriVariables);
    }

    /** @deprecated */
    @Deprecated
    public <T> T exchange(String url, HttpMethod method, Type responseType, Object... uriVariables) {
        HttpEntity<Object> httpEntity = this.getHttpEntity((Object)null);
        return this.executeForObject(url, method, httpEntity, responseType, uriVariables);
    }

    /** @deprecated */
    @Deprecated
    public <T> T exchange(String url, HttpMethod method, Type responseType) {
        HttpEntity<Object> httpEntity = this.getHttpEntity((Object)null);
        return this.executeForObject(url, method, httpEntity, responseType);
    }

    /** @deprecated */
    @Deprecated
    public <T> T exchange(String url, HttpMethod method, Type responseType, Object requestBody, HttpHeaders requestHeaders, Map<String, ?> uriVariables) {
        HttpEntity<Object> requestEntity = new HttpEntity(requestBody, requestHeaders);
        return this.executeForObject(url, method, requestEntity, responseType, uriVariables);
    }

    /** @deprecated */
    @Deprecated
    public <T> T exchange(String url, HttpMethod method, Type responseType, Object requestBody, HttpHeaders requestHeaders, Object... uriVariables) {
        HttpEntity<Object> requestEntity = new HttpEntity(requestBody, requestHeaders);
        return this.executeForObject(url, method, requestEntity, responseType, uriVariables);
    }

    /** @deprecated */
    @Deprecated
    public <T> T exchange(String url, HttpMethod method, Object requestBody, Type responseType, HttpHeaders requestHeaders) {
        HttpEntity<Object> requestEntity = new HttpEntity(requestBody, requestHeaders);
        return this.executeForObject(url, method, requestEntity, responseType);
    }

    /** @deprecated */
    @Deprecated
    public <T> T exchange(String url, HttpMethod method, Type responseType, Object requestBody, Map<String, ?> uriVariables) {
        HttpEntity<Object> requestEntity = new HttpEntity(requestBody, (MultiValueMap)null);
        return this.executeForObject(url, method, requestEntity, responseType, uriVariables);
    }

    /** @deprecated */
    @Deprecated
    public <T> T exchange(String url, HttpMethod method, Type responseType, Object requestBody, Object... uriVariables) {
        HttpEntity<Object> requestEntity = new HttpEntity(requestBody, (MultiValueMap)null);
        return this.executeForObject(url, method, requestEntity, responseType, uriVariables);
    }

    /** @deprecated */
    @Deprecated
    public <T> T exchange(String url, HttpMethod method, Object requestBody, Type responseType) {
        HttpEntity<Object> requestEntity = new HttpEntity(requestBody, (MultiValueMap)null);
        return this.executeForObject(url, method, requestEntity, responseType);
    }
}
